package com.nineclock.sign.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.crypto.asymmetric.Sign;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.nineclock.common.enums.ResponseEnum;
import com.nineclock.common.exception.NcException;
import com.nineclock.common.threadlocal.CurrentUserHolder;
import com.nineclock.common.utils.BeanHelper;
import com.nineclock.sign.dto.SignAllUserDTO;
import com.nineclock.sign.dto.SignDTO;
import com.nineclock.sign.mapper.SignMapper;
import com.nineclock.sign.mapper.SignPictureMapper;
import com.nineclock.sign.pojo.SignInfo;
import com.nineclock.sign.pojo.SignPicture;
import com.nineclock.sign.service.SignService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.nineclock.common.enums.ResponseEnum;
import com.nineclock.common.exception.NcException;
import com.nineclock.common.threadlocal.CurrentUserHolder;
import com.nineclock.common.utils.BeanHelper;
import com.nineclock.sign.dto.SignAllUserDTO;
import com.nineclock.sign.dto.SignDTO;
import com.nineclock.sign.mapper.SignInfoMapper;
import com.nineclock.sign.mapper.SignMapper;
import com.nineclock.sign.mapper.SignPictureMapper;
import com.nineclock.sign.pojo.SignInfo;
import com.nineclock.sign.pojo.SignPicture;
import com.nineclock.sign.service.SignService;
import com.nineclock.system.dto.SysCompanyUserDTO;
import com.nineclock.system.feign.SysOrganizationFeign;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class SignServiceImpl implements SignService {

    @Autowired
    SignMapper signMapper;

    @Autowired
    SignPictureMapper signPictureMapper;

    @Autowired
    RedisTemplate<String, String> redisTemplate;



    @Autowired
    SignInfoMapper signInfoMapper;



    @Autowired
    SysOrganizationFeign organizationFeign;


    @Override//签到
    public void sign(SignDTO signDTO) {

        if (signDTO == null){
            throw new NcException(ResponseEnum.INVALID_PARAM_ERROR);
        }
        //复制 DTO为pojo
        SignInfo signInfo = BeanHelper.copyProperties(signDTO,SignInfo.class);

        //从当前线程获取企业id和用户id
        signInfo.setCompanyId(CurrentUserHolder.get().getCompanyId());
        signInfo.setCompanyUserId(CurrentUserHolder.get().getCompanyUserId());



        //设置时间
        signInfo.setCreateDate(DateUtil.format(new Date(),"yyyy-MM-dd"));
        signInfo.setCreateMonth(DateUtil.format(new Date(),"yyyy-MM"));
        signInfo.setCreateTime(new Date());

       /* //设置前端传来的数据
        signInfo.setRemark(signDTO.getRemark());//备注
        signInfo.setAddress(signDTO.getAddress());
        signInfo.setImei(signDTO.getImei());//手机标识
        signInfo.setVisit(signDTO.getVisit());//拜访对象
        signInfo.setLat(signDTO.getLat());
        signInfo.setLng(signDTO.getLng());*/

        //保存签到记录到数据库
        signInfoMapper.insert(signInfo);

       //添加到ridis中
      String key= signDTO.getCreateDate() + "-"+signInfo.getCompanyUserId();
        String count = redisTemplate.boundValueOps(key).get();
        if (count==null){
            redisTemplate.boundValueOps(key).set("1",1, TimeUnit.DAYS);
        }else {
            redisTemplate.boundValueOps(key).set(String.valueOf(Integer.parseInt(count)+1));
        }




        SignPicture signPicture = new SignPicture();
        String[] url = signDTO.getImages().split(",");//获取签到图片列表 - ,分割
        for (String path : url) {
            signPicture.setSignId(signInfo.getId());//图片的id
            signPicture.setUrl(path);//图片的路径
            signPictureMapper.insert(signPicture);
        }

    }


    @Override
    public Integer examine() {

        LambdaQueryWrapper<SignInfo> wrapper = new LambdaQueryWrapper<>();
        //当前时间
        String now = DateUtil.format(new Date(), "yyyy-MM-dd");
        wrapper.eq(SignInfo::getCreateDate,now);
        //从当前线程获取用户和企业id
        wrapper.eq(SignInfo::getCompanyId,CurrentUserHolder.get().getCompanyId());
        wrapper.eq(SignInfo::getCompanyUserId,CurrentUserHolder.get().getCompanyUserId());
        //获取签到次数
        Integer integer = signInfoMapper.selectCount(wrapper);


        return integer;
    }






    /**
     * APP:查询本月签到记录
     *
     * @param dateStr
     * @return
     */
    @Override
    public List<SignDTO> pagingSignCurrentUserByMonth(String dateStr) {
        /*
        如果前端没有传入日期字符串，分两种情况，看一下前端传入的参数值
        1.返回参数错误提示信息
        2.用户直接点开之后，直接默认显示当月的信息即可，
          当传入要查询月份时，返回对应查询日期的签到信息
         */

        if (dateStr == null) {
            throw new NcException(ResponseEnum.INVALID_PARAM_ERROR);
        }
        //因为前端会直接传过来截取好的年份加月份传给后端，所以dateStr就是要查询的日期对应年份月份
//        String createMonthStr = this.getCreateMonthStr(date);
        //根据当前用户id，以及签到日期必须在日期集合范围内，获取到当前登录用户的签到信息集合
        LambdaQueryWrapper<SignInfo> signInfoQw = new LambdaQueryWrapper<>();
        signInfoQw.eq(SignInfo::getCompanyUserId, CurrentUserHolder.get().getCompanyUserId());
        signInfoQw.eq(SignInfo::getCompanyId, CurrentUserHolder.get().getCompanyId());
        signInfoQw.eq(SignInfo::getCreateMonth, dateStr);
        List<SignInfo> signInfos = signInfoMapper.selectList(signInfoQw);
//        //设置集合中的元素默认从高到低排序，返回结果
//        signDTOS.sort(new Comparator<SignDTO>() {
//            @Override
//            public int compare(SignDTO o1, SignDTO o2) {
//                return o1.getCreateTime().compareTo(o2.getCreateTime());
//            }
//        });
//        //遍历每个元素，设置当月签到次数属性值
//        List<SignDTO> list = new ArrayList<>();
//        for (SignDTO signDTO : signDTOS) {
//            signDTO.setMonthCount(Integer.toString(signDTOS.size()));
//            list.add(signDTO);
//        }


        //由于signinfo中的部分属性与SignDTO的属性相同，所以可以使用工具类将相同的属性转换为结果集合
        List<SignDTO> signDTOList = BeanHelper.copyWithCollection(signInfos, SignDTO.class);
        //给signInfo和SignDTO中缺少的属性赋值
        List<SignDTO> signDTOS = signDTOList.stream().map(signDTO -> {
            signDTO.setUserName(CurrentUserHolder.get().getUsername());
            return signDTO;
        }).collect(Collectors.toList());
        signDTOList = this.getSignDTOList(signDTOS);
        return signDTOList;
    }


    /**
     * 将获取到的signInfo集合通过工具类转换成signDTO类型的集合，
     * 并完成signDTO集合中所有元素的signPictureUrlList和images属性的赋值
     * //////////////////////////////////////////////////////////////////////
     * 还有当日签到次数的设置，直接调用本类中方法得到值
     * //////////////////////////////////////////////////////////////////////
     * （对signPictureUrlList用来存储图片地址的属性完成赋值，返回SignDTO类型的签到信息集合）
     * 返回结果为SignDTO类型的集合
     */
    private List<SignDTO> getSignDTOList(List<SignDTO> signDTOS) {


        //遍历获取到的结果集合，将其中的与图片关联的属性查出并赋值，然后加到每个元素中，最后将每个元素添加到结果集合中
        if (CollectionUtil.isNotEmpty(signDTOS)) {
            //判断非空后遍历
            signDTOS.forEach(signDTO -> {

                //每个signinfo中都需要一个signPictureUrlList用来存储图片地址
                List<String> signPictureUrlList = new ArrayList<>();
                //创建查询条件，查询SignPicture,得到图片地址集合
                LambdaQueryWrapper<SignPicture> signPictureQw = new LambdaQueryWrapper<>();
                signPictureQw.eq(SignPicture::getSignId, signDTO.getId());//匹配签到签到信息中的id
                List<SignPicture> signPictures = signPictureMapper.selectList(signPictureQw);//查出与签到信息中的id对应的所有图片集合
                if (CollectionUtil.isNotEmpty(signPictures)) {//判断非空
                    for (SignPicture signPicture : signPictures) {//遍历查到的与签到信息中的id对应的所有图片集合
                        signPictureUrlList.add(signPicture.getUrl());//获取到每个signPicture的url字符串路径存储到图片地址集合中
                    }//循环结束后，所有与签到信息中的id对应的所有图片地址都已经存好了
                    //完成signinfo集合中所有元素的signPictureUrlList属性的赋值
                    signDTO.setSignPictureUrlList(signPictureUrlList);
                    //对图片地址集合中的数据做分隔，逗号分隔，设置images属性
                    String images = signPictureUrlList.stream().collect(Collectors.joining(","));
                    signDTO.setImages(images);//给images属性赋值
                }
                // TODO: 2021/6/29 当日签到次数
                //获取当日签到次数
                Integer examine = this.examine();
                //赋值DayCount属性
                signDTO.setDayCount(examine.toString());

            });
        }
        return signDTOS;
    }

    /**
     * APP: 获取团队员工打卡列表
     * 查询指定日期, 当前企业所有员工的签到数据 （从sign库中的sys_user表中查所有外勤人员）, 查询外勤员工的分布及打卡记录 ;
     * 思路：创建结果集合，
     * 查询出所有外勤人员，没有相关的pojo类，就直接在Mapper中使用SQL语句进行搜索无条件搜索
     * 根据团队中每个人的用户id和当前日期字符串，查询出签到信息
     * 将信息传入传入结果集合中的SignDto对象中，
     * 多名成员将成员姓名和签到信息集合对应好，并赋值对应属性
     * 共同组成集合signResponseList中的数据
     *
     * @param dateStr
     * @return
     */
    @Override
    public List<SignAllUserDTO> listSignAllUser(String dateStr) {
        //前端传入参数的格式为：2021-06-28，与signInfo中的createDate属性格式相同

        /*
        新建结果集合
        */
        List<SignAllUserDTO> signAllUserDTOList = new ArrayList<>();

        /*
        通过条件查询出当前企业符合签到日期要求的所有签到信息
         */
        LambdaQueryWrapper<SignInfo> signinfoQw = new LambdaQueryWrapper<>();
        signinfoQw.eq(SignInfo::getCompanyId, CurrentUserHolder.get().getCompanyId());//匹配企业id
        signinfoQw.eq(SignInfo::getCreateDate, dateStr);//匹配创建日期
        //获取到所有外勤人员在指定日期的签到信息集合
        List<SignInfo> signInfos = signInfoMapper.selectList(signinfoQw);

        //因为一个用户对应着多个签到信息，在赋值username属性时需要去重，使一个username对应多个签到信息
        Set<SysCompanyUserDTO> userDTOSet = signInfos.stream().map(signInfo -> {
            // 通过每个元素的companyId和companyUserId两个属性调用接口找到用户信息
            SysCompanyUserDTO userDTO = organizationFeign.queryCompanyUserByCompanyIdAndCompanUserId(signInfo.getCompanyUserId(), signInfo.getCompanyId()).getData();
            return userDTO;
        }).collect(Collectors.toSet());


        //遍历set集合，username作为主键，使用用户信息中的companyId和userId进行对应，如果符合要求的放入集合中

        //将获取到的signInfo集合通过工具类转换成signDTO类型的集合，并完成signDTO集合中所有元素的signPictureUrlList属性的赋值
        List<SignDTO> signDTOS = BeanHelper.copyWithCollection(signInfos, SignDTO.class);
        signDTOS = this.getSignDTOList(signDTOS);

        //遍历，获取属性值，赋值
        for (SysCompanyUserDTO sysCompanyUserDTO : userDTOSet) {
            //新建结果集合中的元素实例
            SignAllUserDTO signAllUserDTO = new SignAllUserDTO();
            //
            signAllUserDTO.setUsername(signAllUserDTO.getUsername());
            //封装条件搜索同一个员工在指定日期内的所有签到信息
            LambdaQueryWrapper<SignInfo> signInfoLambdaQueryWrapper = new LambdaQueryWrapper<>();
            signInfoLambdaQueryWrapper.eq(SignInfo::getCompanyId, sysCompanyUserDTO.getCompanyId());
            signInfoLambdaQueryWrapper.eq(SignInfo::getCompanyUserId, sysCompanyUserDTO.getId());
            List<SignInfo> signInfos1 = signInfoMapper.selectList(signInfoLambdaQueryWrapper);
            signDTOS = BeanHelper.copyWithCollection(signInfos1, SignDTO.class);
            signAllUserDTO.setSignResponseList(signDTOS);

            signAllUserDTOList.add(signAllUserDTO);

        }
        return signAllUserDTOList;
    }

}


